/*
 * Adara WebCam TimeLapse capture software.
 * Copyright (C) 2012 AdaraProjectTeam
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package adara.plugins.videoglue;

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;

import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.ICodec;

import adara.plugins.Plugin;
import adara.plugins.PluginManager;

/**
 * Implement the gluing of the images into a video. 
 * @author Andrei Ermicioi (aka erani) <andrei.ermicioi@gmail.com>
 *
 */
public class VideoGluePlugin extends Plugin {
	
	private static final Logger logger = 
			Logger.getLogger(VideoGluePlugin.class);
	
	private static final int FRAME_RATE = 24;
	
	private boolean videoStreamInited;

	private List<BufferedImage> images;
	private IMediaWriter writer;
	private long frameTime;
	private long stepSize;
	
	@Override
	public void init(int timegap, File storage) {
		this.images = new LinkedList<BufferedImage>();
		
		String file = storage.getAbsolutePath() + File.separator + "output.mp4";
		writer = ToolFactory.makeWriter(file);
		videoStreamInited = false;
		
		long frameRate = PluginManager.getProperty().getInt(
				"videoglue.frame-rate", FRAME_RATE);
		
		frameTime = 0;
		
		stepSize = TimeUnit.MILLISECONDS.toNanos(1000 / frameRate);
		
		if (logger.isTraceEnabled()) 
			logger.trace(getClass().getSimpleName() + " Initiated");
		
		if (logger.isDebugEnabled())
			logger.debug("Output goes to: " + file);
	}

	

	@Override
	public void close() {
		if (logger.isTraceEnabled())
			logger.trace("Closing plugin " + getClass().getName());
		
		writer.close();
		super.close();
	}
	
	@Override
	protected void update(BufferedImage image) {
		synchronized (images) {
			images.add(image);
		}
	}

	@Override
	protected void process() {
		BufferedImage[] iarr = null; 
		
		synchronized (images) {
			iarr = images.toArray(new BufferedImage[images.size()]);
			images.clear();
		}
		
		if (logger.isDebugEnabled()) 
			logger.debug("Processing a bunch of " + iarr.length);
		
		for (BufferedImage image : iarr) {
			attachImage(image);
		}
	}
	
	private void attachImage(BufferedImage image) {
		BufferedImage bgrScreen = convertToType(image,
				BufferedImage.TYPE_3BYTE_BGR);
		
		if (!videoStreamInited) {
			setupVideoStream(writer, bgrScreen);
		}
		
		writer.encodeVideo(0, bgrScreen, frameTime, TimeUnit.NANOSECONDS);
		frameTime += stepSize;
	}
	
	private BufferedImage convertToType(BufferedImage sourceImage,
			int targetType) {

		BufferedImage image;

		if (sourceImage.getType() == targetType) {
			image = sourceImage;
		} else {
			image = new BufferedImage(sourceImage.getWidth(),
					sourceImage.getHeight(), targetType);
			image.getGraphics().drawImage(sourceImage, 0, 0, null);
		}

		return image;
	}
	
	private void setupVideoStream(IMediaWriter writer, BufferedImage image) {
		videoStreamInited = true;
		
		int width = image.getWidth();
		int height = image.getHeight();
		
		if (PluginManager.getProperty().containsKey("videoglue.width")
				&& PluginManager.getProperty().containsKey("videoglue.height")) {
			
			width = PluginManager.getProperty().getInt("videoglue.width");
			height = PluginManager.getProperty().getInt("videoglue.height");
		}
		
		
		writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4, width, height);
		
		if (logger.isDebugEnabled()) 
			logger.debug("Video resolution: " + width + "x" + height);
	}
}
